package com.xz.coursearrange.GA.impl;

import com.xz.coursearrange.common.ConstantInfo;
import com.xz.coursearrange.entity.ClassTask;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * @Description 表示单个候选键解及其染色体
 * 管理和创建种群的个体
 * <p>
 * gene 基因
 * <p>
 * 同一时间段，班级不同，教师不同，
 */
@Slf4j
@Data
public class Individual {
    /**
     * 每周要上课的天数
     */
    private static final int DAYS = 5;

    /**
     * 每天要上课几节课
     */
    private static final int PERIODS_PER_DAY = 8;

    /**
     * 最大上课数
     */
    public static final int TOTAL_PERIODS = DAYS * PERIODS_PER_DAY;

    /**
     * 染色体
     * key 班级
     * value 记录多段信息基因  	每个班级的课表（40节课数组）
     */
    private Map<String, String[]> chromosome;

    /**
     * 适应度
     */
    private double fitness = 0.0;

    public Individual() {
        this.chromosome = new HashMap<>();
    }


    /**
     * 根据课程计划 创建 染色体
     * 分为固定时间与不固定时间
     * gene 基因
     * 创建染色体
     * 编码规则: （位）
     * 班级编号：8 key
     * 固定时间：1
     * 年级编号：2
     * 班级编号: 8
     * 教师编号：5
     * 课程编号：6
     * 课程属性：2
     * 教室编号 后面添加
     *
     * @param classTaskList 课程计划列表
     */
    public Individual(List<ClassTask> classTaskList) {
        // 初始化染色体
        chromosome = new HashMap<>();

        // 分离固定时间和非固定时间的课程任务
        List<ClassTask> fixedTimeTasks = new ArrayList<>();
        List<ClassTask> flexibleTimeTasks = new ArrayList<>();

        for (ClassTask classTask : classTaskList) {
            if (classTask.getIsFix().equals(ConstantInfo.FIX_TIME_FLAG)) {
                fixedTimeTasks.add(classTask);
            } else {
                flexibleTimeTasks.add(classTask);
            }
        }

        // 初始化每个班级的课程表
        for (ClassTask task : classTaskList) {
            chromosome.put(task.getClassNo(), new String[TOTAL_PERIODS]);
        }

        // 放置固定时间的课程任务
        for (ClassTask fixedTask : fixedTimeTasks) {
            String classNo = fixedTask.getClassNo();
            String[] schedule = chromosome.get(classNo);
            int[] fixedPeriods = GAUtils.getFixTime(fixedTask.getClassTime());
            for (int period : fixedPeriods) {
                schedule[period] = GAUtils.formatCourseInfo(fixedTask);
            }
        }

        // 打乱非固定时间的课程任务
        Collections.shuffle(flexibleTimeTasks);

        // 放置非固定时间的课程任务
        for (ClassTask flexibleTask : flexibleTimeTasks) {
            String classNo = flexibleTask.getClassNo();
            String[] schedule = chromosome.get(classNo);
            int periodsNeeded = flexibleTask.getWeeksNumber();
            int placedPeriods = 0;
            /**
             * 将还没配分配的0-40节课加入集合然后随机打乱，
             * 循环这个课程一周上课的数量，然后挑出随机后的上课时间放入课程
             */
            List<Integer> availablePeriods = new ArrayList<>();
            for (int i = 0; i < TOTAL_PERIODS; i++) {
                if (schedule[i] == null) {
                    availablePeriods.add(i);
                }
            }
            Collections.shuffle(availablePeriods);

            for (int i = 0; i < availablePeriods.size() && placedPeriods < periodsNeeded; i++) {
                schedule[availablePeriods.get(i)] = GAUtils.formatCourseInfo(flexibleTask);
                placedPeriods++;
            }
        }
    }

    public String getIsFix(String classNo, int time) {
        if (chromosome.get(classNo)[time] != null){
            return chromosome.get(classNo)[time].substring(0, 1);
        }
        return null;
    }

    public String getGradeNo(String classNo, int time) {
        if (chromosome.get(classNo)[time] != null) {
            return chromosome.get(classNo)[time].substring(1, 3);
        }
        return null;
    }

    public String getClassNo(String classNo, int time) {
        if (chromosome.get(classNo)[time] != null) {
            return chromosome.get(classNo)[time].substring(3, 11);
        }
        return null;
    }

    public String getTeacherNo(String classNo, int time) {
        if (chromosome.get(classNo)[time] != null){
            return chromosome.get(classNo)[time].substring(11, 16);
        }
        return null;
    }

    public String getCourseNo(String classNo, int time) {
        if (chromosome.get(classNo)[time] != null){
            return chromosome.get(classNo)[time].substring(16, 22);
        }
        return null;
    }

    public String getCourseAttr(String classNo, int time) {
        if (chromosome.get(classNo)[time] != null){
            return chromosome.get(classNo)[time].substring(22, 24);
        }
        return null;
    }

    public String getCourseInfo(String classNo, int time) {
        return chromosome.get(classNo)[time];
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Individual)) return false;
        Individual that = (Individual) o;
        return Double.compare(fitness, that.fitness) == 0 && Objects.equals(chromosome, that.chromosome);
    }

    @Override
    public int hashCode() {
        return Objects.hash(chromosome, fitness);
    }

    public void outString() {
        if (chromosome == null || chromosome.isEmpty()) {
            System.out.println("Chromosome is empty.");
            return;
        }

        for (Map.Entry<String, String[]> entry : chromosome.entrySet()) {
            String classNo = entry.getKey();
            String[] schedule = entry.getValue();

            System.out.println("Class: " + classNo);
            for (int i = 0; i < schedule.length; i++) {
                System.out.println("  Period " + (i + 1) + ": " + (schedule[i] == null ? "Free" : schedule[i]));
            }
            System.out.println();
        }
    }
}
